#include "MemLeak.h"
#include "TString.h"
#include <crtdbg.h>
#include <Psapi.h>
#include <time.h>
#include <fstream>
#include <tchar.h>
#include <assert.h>

#ifdef _DEBUG

TCHAR                g_OutputfileName[MAX_PATH] = { 0 };

#define _CRTBLD
#include "dbgint.h"

#pragma comment(lib, "Psapi.lib")
#pragma comment(lib, "imagehlp.lib")
#pragma comment(lib, "Dbghelp.lib")

void ShowMemleakOperateDlg();
void CloseMemleakOperateDlg();

#define MLD_TRACEINFO_EMPTY            _T("")
#define MLD_TRACEINFO_NOSYMBOL        _T("?(?)")

CInitMemLeak::CInitMemLeak(bool bShowOperateDlg /*= true*/)
{
    InitMemLeak();
    if (bShowOperateDlg)
    {
        ShowMemleakOperateDlg();
    }
}

CInitMemLeak::~CInitMemLeak()
{
    CloseMemleakOperateDlg();
    UninitMemLeak();
}

CMemLeak::CMemLeak()
: m_dwOccurance(0)
, m_bSelfAlloc(false)
, m_bLoopAlloc(false)
, m_VirtualAllocIoccurance(0)
{
}

CMemLeak::~CMemLeak()
{

}

void SymbolPaths(__in_opt TCHAR* lpszSymbolPath = NULL)
{
    TCHAR lpszPath[MAX_PATH];
    _tcscpy_s(lpszSymbolPath, MAX_PATH, _T(".;..\\;..\\..\\"));

    if ( GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), lpszPath, MAX_PATH ) ) {
        _tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
        _tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
    }

    if ( GetEnvironmentVariable( _T("_NT_ALTERNATE_SYMBOL_PATH"), lpszPath, MAX_PATH ) ) {
        _tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
        _tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
    }

    if ( GetEnvironmentVariable( _T("SYSTEMROOT"), lpszPath, MAX_PATH ) ) {
        _tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
        _tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath);
        _tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));

        _tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
        _tcscat_s( lpszSymbolPath, MAX_PATH, _T("\\System32"));
    }
}

#ifdef UNICODE
#define SymInitializeT SymInitializeW
#else
#define SymInitializeT SymInitialize
#endif

BOOL CMemLeak::InitSymInfo(__in_opt TCHAR* lpszUserSymbolPath)
{
    TCHAR lpszSymbolPath[MAX_PATH] = { 0 };
    DWORD symOptions = SymGetOptions();

    symOptions |= SYMOPT_LOAD_LINES; 
    symOptions &= ~SYMOPT_UNDNAME;
    SymSetOptions(symOptions);

    // Get the search path for the symbol files
    SymbolPaths(lpszSymbolPath);
    
    if ( lpszUserSymbolPath ) {
        _tcscat_s(lpszSymbolPath, MAX_PATH, _T(";"));
        _tcscat_s(lpszSymbolPath, MAX_PATH, lpszUserSymbolPath);
    }

    return SymInitializeT(GetCurrentProcess(), lpszUserSymbolPath, TRUE);
}

void CMemLeak::Init() 
{
    static bool bInited = false;
    if (bInited)
    {
        return;
    }
    bInited = true;
    m_fnRtlCaptureStackBackTrace = (CaptureStackBackTraceType)(GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), "RtlCaptureStackBackTrace"));
    InitSymInfo();

    m_pfnOldCrtAllocHook = _CrtSetAllocHook( MemoryAllocHook );
}

void CMemLeak::Uninit()
{
    OutputDebugString(L"CMemLeak begin Uninit module");

    _CrtSetAllocHook(m_pfnOldCrtAllocHook);

    dumpCurrentMemoryTrace();
    _CrtSetAllocHook(m_pfnOldCrtAllocHook);
    m_mapAllInfo.clear();
    m_mapAllInfoClearing.clear();
    m_mapAllVitualAllocBlockInfo.clear();
    SymCleanup( GetCurrentProcess() );

    OutputDebugString(L"CMemLeak begin Uninit module end");
}

BOOL CMemLeak::symStackTrace(__out STACKFRAMEENTRY* pStacktrace)
{
    if ( m_pfnOldCrtAllocHook == NULL ) {
        return FALSE;
    }

    long StackIndex    = 0;

    void* block[MLD_MAX_TRACEINFO];
    memset(block, 0, sizeof(block));

    USHORT frames = (m_fnRtlCaptureStackBackTrace)(3, 59, (void**)block, NULL);

    for (int i = 0; i < frames; i++) {
        void* InstructionPtr = (void*)block[i];
        pStacktrace[StackIndex].addrPC.Offset    = (decltype(pStacktrace[StackIndex].addrPC.Offset))InstructionPtr;
        pStacktrace[StackIndex].addrPC.Segment    = NULL;
        pStacktrace[StackIndex].addrPC.Mode        = AddrModeFlat;
        StackIndex++;
    }

    pStacktrace[StackIndex].addrPC.Offset = 0;
    pStacktrace[StackIndex].addrPC.Segment = 0;

    return TRUE;
}

void Trace(LPCTSTR lpszFormat, ...)
{
    va_list args;
    va_start(args, lpszFormat);

    int nBuf;
    TCHAR szBuffer[512];

    nBuf = _vstprintf_s(szBuffer, _countof(szBuffer), lpszFormat, args); 

    // was there an error? was the expanded string too long?
    _ASSERT(nBuf >= 0);

    OutputDebugString(szBuffer);

    va_end(args);
}

int __cdecl CMemLeak::MemoryAllocHook (
    int    allocType, 
    void    *userData, 
    size_t size, 
    int    blockType, 
    long    requestNumber, 
    const unsigned char    *filename,
    int    lineNumber
    )
{
    _CrtMemBlockHeader *pCrtHead;
    long prevRequestNumber;

    // internal C library internal allocations
    if ( blockType == _CRT_BLOCK ) {
        return TRUE;
    }

    // check if someone has turned off mem tracing
    if  ((( _CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && 
        (( allocType            == _HOOK_ALLOC)        || 
        ( allocType            == _HOOK_REALLOC))) {
            if ( Instance().m_pfnOldCrtAllocHook ) {
                Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
            }
            return TRUE;
    }

    // protect internal mem trace allocs
    if ( Instance().m_bSelfAlloc ) {
        if (Instance().m_pfnOldCrtAllocHook) {
            Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
        }
        return TRUE;
    }

    
    Instance().m_bSelfAlloc = true;


    switch ( allocType )
    {
    case _HOOK_ALLOC:
        {
            void* pAddr = (void *)requestNumber;
            if(!CMemLeak::Instance().m_bMapClearing)
            {
                if (CMemLeak::Instance().m_mapAllInfo.find(pAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
                    Trace(_T("ERROR!MemoryAllocHook:_HOOK_ALLOC Address(0x%p) already allocated\n"), pAddr);
                    return TRUE;
                }
            }

            AllocBlockInfo ainfo;
            ainfo.address        = pAddr;
            ainfo.lineNumber    = lineNumber;
            ainfo.size            = size;
            ainfo.occurance        = Instance().m_dwOccurance++;

            Instance().symStackTrace(ainfo.traceinfo);

            if ( filename ) {
                _tcsncpy_s(ainfo.fileName, MAX_PATH, TString((const char*)filename).Data(), MAX_PATH);
            }

            if (!Instance().m_bLoopAlloc && !CMemLeak::Instance().m_bMapClearing)
            {
                CMemLeak::Instance().m_mapAllInfo[pAddr] = ainfo;
            }
            else
            {
                CMemLeak::Instance().m_mapAllInfoClearing[pAddr] = ainfo;
            }
        }
        break;

    case _HOOK_REALLOC:
        {
            if (_CrtIsValidHeapPointer(userData))
            {
                pCrtHead = pHdr(userData);
                prevRequestNumber = pCrtHead->lRequest;
                //
                if (pCrtHead->nBlockUse == _IGNORE_BLOCK)
                {
                    if (Instance().m_pfnOldCrtAllocHook)
                    {
                        Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
                    }
                }

                void* pAddr = (void *)requestNumber;
                void* pOldAddr = (void *)prevRequestNumber;
                if (!CMemLeak::Instance().m_bMapClearing)
                {
                    if (CMemLeak::Instance().m_mapAllInfo.find(pOldAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
                        CMemLeak::Instance().m_mapAllInfo.erase(pOldAddr);
                    }
                    else {
                        Trace(_T("ERROR!MemoryAllocHook:_HOOK_REALLOC didn't find Address(0x%08X) to free\n"), pOldAddr);
                    }
                }
                else
                {
                    if (CMemLeak::Instance().m_mapAllInfoClearing.find(pOldAddr) != CMemLeak::Instance().m_mapAllInfoClearing.end()) {
                        CMemLeak::Instance().m_mapAllInfoClearing.erase(pOldAddr);
                    }
                }

                AllocBlockInfo ainfo;
                ainfo.address        = pAddr;
                ainfo.lineNumber    = lineNumber;
                ainfo.size            = size;
                ainfo.occurance        = Instance().m_dwOccurance++;

                Instance().symStackTrace(ainfo.traceinfo);

                if ( filename ) {
                    _tcsncpy_s(ainfo.fileName, MAX_PATH, TString((const char*)filename).Data(), MAX_PATH);
                }

                if (!Instance().m_bLoopAlloc && !CMemLeak::Instance().m_bMapClearing)
                {
                    CMemLeak::Instance().m_mapAllInfo[pAddr] = ainfo;
                }
                else
                {
                    CMemLeak::Instance().m_mapAllInfoClearing[pAddr] = ainfo;
                }
            }
        }
        break;

    case _HOOK_FREE:
        {
            if (_CrtIsValidHeapPointer(userData))
            {
                pCrtHead = pHdr(userData);
                requestNumber = pCrtHead->lRequest;
                //
                if (pCrtHead->nBlockUse == _IGNORE_BLOCK) {
                    if (Instance().m_pfnOldCrtAllocHook) {
                        Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
                    }
                }

                AllocBlockInfo ainfo;

                void* pAddr = (void *)requestNumber;
                if (!CMemLeak::Instance().m_bMapClearing)
                {
                    if (CMemLeak::Instance().m_mapAllInfo.find(pAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
                        CMemLeak::Instance().m_mapAllInfo.erase(pAddr);
                    }
                    else {
                        Trace(_T("ERROR!MemoryAllocHook:_HOOK_FREE didn't find Address(0x%08X) to free\n"), pAddr);
                    }
                }
                else
                {
                    if (CMemLeak::Instance().m_mapAllInfoClearing.find(pAddr) != CMemLeak::Instance().m_mapAllInfoClearing.end()) {
                        CMemLeak::Instance().m_mapAllInfoClearing.erase(pAddr);
                    }
                }
            }
        }
        break;
    }

    Instance().m_bSelfAlloc = false;
    return TRUE;
}

void CMemLeak::DeleteOldTempFiles(const TCHAR* dir, const TCHAR* type, int days)
{
    union tu {
        FILETIME fileTime;
        ULARGE_INTEGER ul;
    };    // Seems simplest way to do the Win32 time manipulation.

    WIN32_FIND_DATA FindFileData;
    HANDLE hFind = INVALID_HANDLE_VALUE;

    TCHAR curdir[MAX_PATH];
    GetCurrentDirectory(MAX_PATH, curdir);
    SetCurrentDirectory(dir);

    hFind = FindFirstFile(type, &FindFileData);

    if ( hFind != INVALID_HANDLE_VALUE ) {

        SYSTEMTIME st;
        tu ft;

        GetSystemTime(&st);
        SystemTimeToFileTime(&st, &ft.fileTime);

        while ( FindNextFile(hFind, &FindFileData) != 0 ) {

            if (FILE_ATTRIBUTE_DIRECTORY != FindFileData.dwFileAttributes) {
                tu t;
                t.fileTime = FindFileData.ftCreationTime;

                __int64 delta = (ft.ul.QuadPart - t.ul.QuadPart) / 10000000;    // Seconds.
                int ddays = (int)(delta /= (24 * 3600));

                if ( ddays >= days ) {
                    DeleteFile(FindFileData.cFileName);
                }
            }
        }
        FindClose(hFind);
    }
    SetCurrentDirectory(curdir);
}

BOOL CMemLeak::symFunctionInfoFromAddresses (
    __in ULONG_PTR fnAddress, 
    __in ULONG_PTR stackAddress, 
    __out_ecount(dwSymbolSize) TCHAR* lpszSymbol,
    __in DWORD dwSymbolSize
    )
{
    const DWORD dwBuffSize = sizeof(IMAGEHLP_SYMBOL)+sizeof(TCHAR)*1024;
    BYTE bBuff[dwBuffSize];
    ::ZeroMemory(bBuff, dwBuffSize);

    PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)bBuff;
    pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
    pSym->MaxNameLength    = 1024;

    // Set the default to unknown
//    _tcscpy_s(lpszSymbol, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
    _sntprintf_s(lpszSymbol, MAX_PATH, _TRUNCATE, MLD_TRACEINFO_NOSYMBOL);

    // Get symbol info for address
#ifdef _WIN64
    DWORD64 dwDisp = 0;
#else
    DWORD dwDisp = 0;
#endif    
    if ( SymGetSymFromAddr(GetCurrentProcess(), fnAddress, &dwDisp, (PIMAGEHLP_SYMBOL)pSym) ) {

        //_tcscpy_s(lpszSymbol, dwSymbolSize, TString(pSym->Name).Data());
        _sntprintf_s(lpszSymbol, dwSymbolSize, _TRUNCATE, TString(pSym->Name).Data());
        return TRUE;
    }

    //create the symbol using the address because we have no symbol
    //_stprintf_s(lpszSymbol, dwSymbolSize, _T("0x%08X"), fnAddress);
    _sntprintf_s(lpszSymbol, dwSymbolSize, _TRUNCATE, _T("0x%08X"), fnAddress);
    return FALSE;
}

#ifdef UNICODE
    typedef IMAGEHLP_MODULEW IMAGEHLP_MODULET;
#define SymGetModuleInfoT SymGetModuleInfoW
#else
    typedef IMAGEHLP_MODULE IMAGEHLP_MODULET;
#define SymGetModuleInfoT SymGetModuleInfo
#endif

BOOL CMemLeak::symModuleNameFromAddress (
    __in ULONG_PTR address, 
    __out_ecount(dwModuleSize) TCHAR* lpszModule, 
    __in DWORD dwModuleSize)
{
    IMAGEHLP_MODULET moduleInfo;
    ::ZeroMemory(&moduleInfo, sizeof(IMAGEHLP_MODULET));
    moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULET);

    if (! SymGetModuleInfoT(GetCurrentProcess(), address, &moduleInfo) ) {
        //_tcscpy_s(lpszModule, dwModuleSize, MLD_TRACEINFO_NOSYMBOL);
        _sntprintf_s(lpszModule, dwModuleSize, _TRUNCATE, MLD_TRACEINFO_NOSYMBOL);
        return FALSE;
    }

    //_tcscpy_s(lpszModule, dwModuleSize, moduleInfo.ModuleName);
    _sntprintf_s(lpszModule, dwModuleSize, _TRUNCATE, moduleInfo.ModuleName);
    return TRUE;
}

BOOL CMemLeak::symSourceInfoFromAddress(
    __in ULONG_PTR address, 
    __out_ecount(dwSourceInfoSize) TCHAR* lpszSourceInfo, 
    __in DWORD dwSourceInfoSize
    )
{
    BOOL           ret = FALSE;
    IMAGEHLP_LINE  lineInfo;
    DWORD          dwDisp;
    TCHAR          lpModuleInfo[MAX_PATH] = { 0 };

    _tcscpy_s(lpszSourceInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

    memset(&lineInfo, 0, sizeof( IMAGEHLP_LINE ));
    lineInfo.SizeOfStruct = sizeof( IMAGEHLP_LINE );

    if ( SymGetLineFromAddr(GetCurrentProcess(), address, &dwDisp, &lineInfo) ) {
        _stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("%s(%d): 0x%08X"), TString(lineInfo.FileName).Data(), lineInfo.LineNumber, address);
        ret = TRUE;
    }
    else {
        symModuleNameFromAddress(address, lpModuleInfo, sizeof(lpModuleInfo)/sizeof(lpModuleInfo[0]));

        if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0') ) {
            _stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("0x%p"), lpModuleInfo, address);    // Tim ???
        }
        else {
            _stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("%sdll! 0x%08X"), lpModuleInfo, address);
        }
        ret = FALSE;
    }

    return ret;
}

void CMemLeak::ClearMemStatistics()
{
    CMemLeak::Instance().m_bMapClearing = true;
    m_mapAllInfo.clear();
    m_mapAllInfo = m_mapAllInfoClearing;
    CMemLeak::Instance().m_bMapClearing = false;
    m_mapAllInfoClearing.clear();
}

void CMemLeak::dumpCurrentMemoryTrace(int iSnapLoopTime)
{
    if (iSnapLoopTime > 0)
    {
        m_bLoopAlloc = true;
        Sleep(iSnapLoopTime);
    }
    //_CrtSetAllocHook(m_pfnOldCrtAllocHook);
    auto mapAllInfo = m_mapAllInfo;
    //m_pfnOldCrtAllocHook = _CrtSetAllocHook(MemoryAllocHook);
    m_bLoopAlloc = false;

    TCHAR                buf[2 * MAX_PATH];
    TCHAR                fileName[MAX_PATH];
    TCHAR                symInfo[MAX_PATH];
    TCHAR                srcInfo[MAX_PATH];
    TCHAR                moduleInfo[MAX_PATH];

    STACKFRAMEENTRY*    p = 0;

    ofstream myfile;

    struct tm timeinfo;
    __time64_t long_time;
    _time64(&long_time);
    // Convert to local time.
    _localtime64_s(&timeinfo, &long_time);

    TCHAR TempDir[MAX_PATH];
    TCHAR ProcName[MAX_PATH];
    GetTempPath(MAX_PATH, TempDir);
    ProcName[0] = _T('\0');
    GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName) / sizeof(TCHAR));

    _stprintf_s(fileName, MAX_PATH, _T("%smlcurrentdetector-(%s)_"), TempDir, ProcName);
    _tcsftime(buf, 2 * MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"), &timeinfo);
    _tcscat_s(fileName, MAX_PATH, buf);

    myfile.open(fileName);
    memcpy(g_OutputfileName, fileName, sizeof(fileName));

    DeleteOldTempFiles(TempDir, _T("mlcurrentdetector-(*.log"), 1);

    _tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
    //_tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

    std::map<__int64, AllocTotalInfo>  mapTotalAlloc;

    map<LPVOID, AllocBlockInfo>::iterator it = mapAllInfo.begin();
    for (; it != mapAllInfo.end(); it++)
    {
        __int64 PcOffset = 0;
        STACKFRAMEENTRY* p1 = 0;
        p1 = &it->second.traceinfo[0];
        while (p1[0].addrPC.Offset)
        {
            PcOffset += p1[0].addrPC.Offset;
            p1++;
        }

        std::string strStack;
        if (mapTotalAlloc.find(PcOffset) == mapTotalAlloc.end())
        {
            p = &it->second.traceinfo[0];
            while (p[0].addrPC.Offset)
            {
                symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
                symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo) / sizeof(srcInfo[0]));
                symModuleNameFromAddress(p[0].addrPC.Offset, moduleInfo, sizeof(moduleInfo) / sizeof(moduleInfo[0]));

                _stprintf_s(buf, 2 * MAX_PATH, _T("Module:%s %s->%s()\n"), moduleInfo, srcInfo, symInfo);
                Trace(_T("%s->%s()\n"), srcInfo, symInfo);

                strStack += TStringA(buf).Data();
                p++;
            }
        }

        if (strStack.empty())
        {
            mapTotalAlloc[PcOffset].dwAllocTimes++;
            mapTotalAlloc[PcOffset].dwTotalsize += it->second.size;
        }
        else
        {
            mapTotalAlloc.insert(std::pair<__int64, AllocTotalInfo>(PcOffset, AllocTotalInfo(1, PcOffset, it->second.size, strStack)));
        }
    }
    
    __int64 totalAllocSize = 0;
    std::multimap<DWORD, tagAllocTotalInfo*> mapAllocTrace;
    for (auto& item : mapTotalAlloc)
    {
        mapAllocTrace.insert(std::pair<DWORD, tagAllocTotalInfo*>(item.second.dwTotalsize, &item.second));
        totalAllocSize += item.second.dwTotalsize;
    }

    for (auto& item : mapAllocTrace)
    {
        _stprintf_s(buf, 2 * MAX_PATH, _T("\n----------------------------------------------------------- total allocSize:%d   allocTimes:%d  pcOffset:%llu\n"), item.first, item.second->dwAllocTimes, item.second->PcOffset);
        Trace(buf);
        myfile << TStringA(buf).Data();

        myfile << item.second->strStack;
    }

    _stprintf_s(buf, 2 * MAX_PATH, _T("\n----------------------------------------------------------- process all total allocSize:%llu \n"), totalAllocSize);
    myfile << TStringA(buf).Data();

    myfile.close();
}

void CMemLeak::dumpCurrentVirtualAllocTrace()
{
    TCHAR                buf[2 * MAX_PATH];
    TCHAR                fileName[MAX_PATH];
    TCHAR                symInfo[MAX_PATH];
    TCHAR                srcInfo[MAX_PATH];
    TCHAR                moduleInfo[MAX_PATH];

    size_t                totalSize = 0;
    int                    numLeaks = 0;
    STACKFRAMEENTRY*    p = 0;

    ofstream myfile;

    struct tm timeinfo;
    __time64_t long_time;
    _time64(&long_time);
    // Convert to local time.
    _localtime64_s(&timeinfo, &long_time);

    TCHAR TempDir[MAX_PATH];
    TCHAR ProcName[MAX_PATH];
    GetTempPath(MAX_PATH, TempDir);
    ProcName[0] = _T('\0');
    GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName) / sizeof(TCHAR));

    _stprintf_s(fileName, MAX_PATH, _T("%smlCurrentVirtualAllocdetector-(%s)_"), TempDir, ProcName);
    _tcsftime(buf, 2 * MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"), &timeinfo);
    _tcscat_s(fileName, MAX_PATH, buf);

    myfile.open(fileName);

    DeleteOldTempFiles(TempDir, _T("smlCurrentVirtualAllocdetector-(*.log"), 1);

    _tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
    _tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

    map<LPVOID, AllocBlockInfo>::iterator it = m_mapAllVitualAllocBlockInfo.begin();
    for (; it != m_mapAllVitualAllocBlockInfo.end(); it++)
    {
        numLeaks++;
        _stprintf_s(buf, MAX_PATH, _T("Memory Leak(%d)------------------->\n"), numLeaks);
        Trace(buf);

        myfile << TStringA(buf).Data();

        if (_tcslen(it->second.fileName) != 0)
        {
            _stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d) %s(%d)\n"),
                it->second.address, it->second.size, it->second.occurance, it->second.fileName, it->second.lineNumber);
        }
        else
        {
            _stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d)\n"),
                it->second.address, it->second.size, it->second.occurance);
        }

        Trace(buf);
        myfile << TStringA(buf).Data();

        p = &it->second.traceinfo[0];
        while (p[0].addrPC.Offset)
        {
            symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
            symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo) / sizeof(srcInfo[0]));
            symModuleNameFromAddress(p[0].addrPC.Offset, moduleInfo, sizeof(moduleInfo) / sizeof(moduleInfo[0]));
            _stprintf_s(buf, 2 * MAX_PATH, _T("Module:%s %s->%s()\n"), moduleInfo, srcInfo, symInfo);
            Trace(_T("%s->%s()\n"), srcInfo, symInfo);

            myfile << TStringA(buf).Data();

            p++;
        }
        totalSize += it->second.size;
    }
    _stprintf_s(buf, 2 * MAX_PATH, _T("\n-----------------------------------------------------------\n"));
    Trace(buf);

    myfile << TStringA(buf).Data();

    if (!totalSize)
    {
        _stprintf_s(buf, 2 * MAX_PATH, _T("No Memory Leaks Detected for Allocations\n\n"));
        Trace(buf);
        myfile << TStringA(buf).Data();
    }
    else
    {
        _stprintf_s(buf, 2 * MAX_PATH, _T("Total %d Memory Leaks: %d bytes Total Alocations %d\n\n"), numLeaks, totalSize, m_dwOccurance);
    }

    Trace(buf);

    myfile << TStringA(buf).Data();

#ifdef UNICODE
    const TCHAR *umb = _T("Unicode");
#else
    const TCHAR *umb = _T("Multibyte");
#endif

#ifdef _WIN64
    const TCHAR *w64 = _T("64 bit");
#else
    const TCHAR *w64 = _T("32 bit");
#endif

#ifdef NDEBUG
    const TCHAR *dbg = _T("release build.");
#else
    const TCHAR *dbg = _T("debug build.");
#endif

    _stprintf_s(TempDir, MAX_PATH, _T("%s %s %s\n"), umb, w64, dbg);

    myfile << TStringA(TempDir).Data();
    Trace(TempDir);

    myfile.close();
    m_VirtualAllocIoccurance = 0;
}

void CMemLeak::VirtualAllocCallsTrace(LPVOID lpAllocAddr, DWORD dwSize)
{
    AllocBlockInfo ainfo;
    ainfo.address = lpAllocAddr;
    ainfo.lineNumber = 0;
    ainfo.size = dwSize;
    ainfo.occurance = m_VirtualAllocIoccurance++;
    Instance().symStackTrace(ainfo.traceinfo);

    m_mapAllVitualAllocBlockInfo[lpAllocAddr] = ainfo;
}

void CMemLeak::VirtualFreeCallsTrace(LPVOID lpAllocAddr)
{
    if (m_mapAllVitualAllocBlockInfo.find(lpAllocAddr) != m_mapAllVitualAllocBlockInfo.end()) {
        m_mapAllVitualAllocBlockInfo.erase(lpAllocAddr);
        m_VirtualAllocIoccurance--;
    }
    else{
        Trace(_T("ERROR!CMemLeak:VirtualFreeCallsTrace didn't find Address(0x%08X) to free\n"), lpAllocAddr);
    }
}

void CMemLeak::dumpMemoryTrace()
{
    TCHAR                buf[2*MAX_PATH];
    TCHAR                fileName[MAX_PATH];
    TCHAR                symInfo[MAX_PATH];
    TCHAR                srcInfo[MAX_PATH];

    size_t                totalSize                        = 0;
    int                    numLeaks                        = 0;
    STACKFRAMEENTRY*    p                                = 0;

    ofstream myfile;

    struct tm timeinfo;
    __time64_t long_time;
    _time64(&long_time);
    // Convert to local time.
    _localtime64_s(&timeinfo, &long_time);

    TCHAR TempDir[MAX_PATH];
    TCHAR ProcName[MAX_PATH];
    GetTempPath(MAX_PATH, TempDir);
    ProcName[0] = _T('\0');
    GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName)/sizeof(TCHAR));

    _stprintf_s(fileName, MAX_PATH, _T("%smldetector-(%s)_"), TempDir, ProcName); 
    _tcsftime(buf,2*MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"),&timeinfo);
    _tcscat_s(fileName,MAX_PATH, buf);

    myfile.open(fileName); 

    DeleteOldTempFiles(TempDir, _T("mldetector-(*.log"), 1);

    _tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
    _tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

    map<LPVOID, AllocBlockInfo>::iterator it = m_mapAllInfo.begin();
    for (; it != m_mapAllInfo.end(); it++)
    {
        numLeaks++;
        _stprintf_s(buf, MAX_PATH, _T("Memory Leak(%d)------------------->\n"), numLeaks);
        Trace(buf);

        myfile << TStringA(buf).Data();

        if (_tcslen(it->second.fileName) != 0)
        {
            _stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d) %s(%d)\n"), 
                it->second.address, it->second.size, it->second.occurance, it->second.fileName, it->second.lineNumber);
        }
        else
        {
            _stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d)\n"), 
                it->second.address, it->second.size, it->second.occurance);
        }

        Trace(buf);
        myfile << TStringA(buf).Data();

        p = &it->second.traceinfo[0];
        while(p[0].addrPC.Offset)
        {
            symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
            symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo)/sizeof(srcInfo[0]));
            _stprintf_s(buf, 2*MAX_PATH, _T("%s->%s()\n"), srcInfo, symInfo);
            Trace(_T("%s->%s()\n"), srcInfo, symInfo);

            myfile << TStringA(buf).Data();

            p++;
        }
        totalSize += it->second.size;
    }
    _stprintf_s(buf, 2*MAX_PATH, _T("\n-----------------------------------------------------------\n"));
    Trace(buf);

    myfile << TStringA(buf).Data();

    if(!totalSize) 
    {
        _stprintf_s(buf, 2*MAX_PATH, _T("No Memory Leaks Detected for %d Allocations\n\n"), m_dwOccurance);
        Trace(buf);
        myfile << TStringA(buf).Data();
    }
    else
    {
        _stprintf_s(buf, 2*MAX_PATH, _T("Total %d Memory Leaks: %d bytes Total Alocations %d\n\n"), numLeaks, totalSize, m_dwOccurance);
    }

    Trace(buf);

    myfile << TStringA(buf).Data();

#ifdef UNICODE
    const TCHAR *umb = _T("Unicode");
#else
    const TCHAR *umb = _T("Multibyte");
#endif

#ifdef _WIN64
    const TCHAR *w64 = _T("64 bit");
#else
    const TCHAR *w64 = _T("32 bit");
#endif

#ifdef NDEBUG
    const TCHAR *dbg = _T("release build.");
#else
    const TCHAR *dbg = _T("debug build.");
#endif

    _stprintf_s(TempDir, MAX_PATH, _T("%s %s %s\n"), umb, w64, dbg);

    myfile << TStringA(TempDir).Data();
    Trace(TempDir);

    myfile.close();
}

#endif // _DEBUG